package com.cartel.genius.dao.impl;

import java.util.Calendar;
import java.util.List;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.cartel.genius.dao.CategoryDAO;
import com.cartel.genius.dao.exceptions.CategoryExistsException;
import com.cartel.genius.dao.exceptions.CategoryNotFoundException;
import com.cartel.genius.dao.exceptions.CategoryParentNotFoundException;
import com.cartel.genius.dto.CategoryTO;
import com.cartel.genius.entities.CategoryEntity;
import com.cartel.genius.util.BeanTransformer;
import com.mysql.jdbc.StringUtils;

/**
 * DAO implementation class for Category (place divisions) operations
 * 
 * @author Naresh Reddy
 */
public class CategoryDAOImpl extends HibernateDaoSupport implements CategoryDAO {
	
	private static final Logger logger = LoggerFactory.getLogger(CategoryDAOImpl.class);
	
	/**
	 * Method to add root level category. All other categories will be childs of this category.
	 * 
	 * @param categoryName
	 * @return CategoryEntity
	 * @throws CategoryExistsException
	 */
	@Override
	public CategoryTO addRootCategory(String categoryName) throws CategoryExistsException {
		logger.debug("Entered [addRootCategory] of [CategoryDAOImpl]");
		
		// check if the root category already exists
		DetachedCriteria categoryCheckCriteria = DetachedCriteria.forClass(CategoryEntity.class);
		if (getHibernateTemplate().findByCriteria(categoryCheckCriteria).size() > 0) {
			logger.warn("Root category already exists in the system");
			throw new CategoryExistsException("Root category already exists in the system");
		}
		// check if the category name already exists
		// this check will be necessary when there we want to add multiple root level categories
		if(isCategoryNameExists(categoryName)) {
			logger.warn("CategoryName " + categoryName + " already exists in the system");
			throw new CategoryExistsException("CategoryName " + categoryName + " already exists in the system");
		}
		
		CategoryEntity ce = new CategoryEntity();
		ce.setCategoryName(categoryName);
		ce.setCreatedDate(Calendar.getInstance().getTimeInMillis());
		ce.setModifiedDate(Calendar.getInstance().getTimeInMillis());
		
		Integer savedCategoryId = (Integer) getHibernateTemplate().save(ce);
		
//		DetachedCriteria criteria = DetachedCriteria.forClass(CategoryEntity.class);
//		criteria.add(Restrictions.like("categoryName", categoryName));		
//		CategoryEntity parent = (CategoryEntity) getHibernateTemplate().findByCriteria(criteria).get(0);
		
		CategoryEntity parent = getHibernateTemplate().get(CategoryEntity.class, savedCategoryId);
		
		ce.setParentCategory(parent);
		ce.setModifiedDate(Calendar.getInstance().getTimeInMillis());
		getHibernateTemplate().saveOrUpdate(ce);
		
		ce.setCategoryId(savedCategoryId);
		
		logger.debug("Root Category " + ce + " saved successfully in the system");
		
		logger.debug("Exiting [addRootCategory] of [CategoryDAOImpl]");
		
		return BeanTransformer.transformCategory(ce);
	}
	
	/**
	 * Method to check if the category name already exists in the system or not
	 * 
	 * @param categoryName
	 * @return boolean
	 */
	private boolean isCategoryNameExists(String categoryName) {
		DetachedCriteria categoryCheckCriteria = DetachedCriteria.forClass(CategoryEntity.class);
		categoryCheckCriteria.add(Restrictions.like("categoryName", categoryName));
		return getHibernateTemplate().findByCriteria(categoryCheckCriteria).size() > 0 ? true : false;		
	}
	
	@SuppressWarnings("unchecked")
	private List<CategoryEntity> getCategoryEntities(int categoryId, String categoryName) throws CategoryNotFoundException {
		DetachedCriteria categoryCriteria = DetachedCriteria.forClass(CategoryEntity.class);
		
		boolean categoryFlag = false;
		if (categoryId != 0) {
			categoryCriteria.add(Restrictions.idEq(categoryId));
			categoryFlag = true;
		}
		
		if (!StringUtils.isNullOrEmpty(categoryName)) {
			categoryCriteria.add(Restrictions.like("categoryName", categoryName));
			categoryFlag = true;
		}
		
		List<CategoryEntity> categoryList = null;
		
		if (categoryFlag) {
			categoryList = getHibernateTemplate().findByCriteria(categoryCriteria);
			
			if (categoryList == null || categoryList.size() == 0) {
				logger.warn("Category not found in the system");
				throw new CategoryNotFoundException("Category not found in the system");
			}
		} else {
			logger.warn("Category is not present");
			throw new CategoryNotFoundException("Category is not present");
		}
		return categoryList;
	}
	
	/**
	 * Method to add child category under specified parent
	 * @param categoryName
	 * @param parentCategory
	 * @return CategoryEntity
	 * @throws CategoryExistsException
	 * @throws CategoryNotFoundException 
	 */
	@Override
	public CategoryTO addCategory(String categoryName, CategoryTO parentCategory) throws CategoryExistsException, CategoryParentNotFoundException {
		logger.debug("Entered [addCategory] of [CategoryDAOImpl]");
		
		List<CategoryEntity> parentCategoryList;
		try {
			parentCategoryList = getCategoryEntities(parentCategory.getCategoryId(), parentCategory.getCategoryName());
		} catch (CategoryNotFoundException e) {
			logger.warn("Category not found in the system");
			throw new CategoryParentNotFoundException(e);
		}
		
		// check if the category name already exists
		if(isCategoryNameExists(categoryName)) {
			logger.warn("CategoryName " + categoryName + " already exists in the system");
			throw new CategoryExistsException("CategoryName " + categoryName + " already exists in the system");
		}
		
		CategoryEntity categoryEntity = new CategoryEntity();
		categoryEntity.setCategoryName(categoryName);
		categoryEntity.setParentCategory(parentCategoryList.get(0));
		categoryEntity.setCreatedDate(Calendar.getInstance().getTimeInMillis());
		categoryEntity.setModifiedDate(Calendar.getInstance().getTimeInMillis());
		
		Integer savedCategoryId = (Integer) getHibernateTemplate().save(categoryEntity);
		
		categoryEntity.setCategoryId(savedCategoryId);
		
		logger.debug("Category " + categoryEntity + " saved in the system successfully");
		
		logger.debug("Exiting [addCategory] of [CategoryDAOImpl]");
		
		return BeanTransformer.transformCategory(categoryEntity);
	}	
	
	/**
	 * Method to modify category (should be used for modifying category name)
	 * @param category
	 * @return CategoryEntity
	 * @throws CategoryNotFoundException
	 */
	@Override
	public CategoryTO modifyCategory(CategoryTO category) throws CategoryNotFoundException {
		// TODO
		throw new UnsupportedOperationException();
	}
	
	/**
	 * Method to modify category hierarchy
	 * @param categoryName
	 * @param oldParentId
	 * @param newParentId
	 * @return CategoryEntity
	 * @throws CategoryNotFoundException
	 * @throws CategoryParentNotFoundException
	 */
	@Override
	public CategoryTO modifyCategory(String categoryName, int oldParentId, int newParentId) throws CategoryNotFoundException, CategoryParentNotFoundException {
		// TODO
		throw new UnsupportedOperationException();
	}
	
	/**
	 * Method to remove a Category from system
	 * @param categoryId
	 * @return boolean
	 * @throws CategoryNotFoundException
	 */
	@Override
	public boolean removeCategory(int categoryId) throws CategoryNotFoundException {
		// TODO
		throw new UnsupportedOperationException();
	}
	
	/**
	 * Method to remove a Category from system
	 * @param category
	 * @return boolean
	 * @throws CategoryNotFoundException
	 */
	@Override
	public boolean removeCategory(CategoryTO category) throws CategoryNotFoundException {
		// TODO
		throw new UnsupportedOperationException();
	}	

}
